﻿using System;
using System.Collections.Generic;
using System.Linq;
using Autofac;
using Orchard.Tests;
using Orchard.DisplayManagement.Descriptors;
using Orchard.DisplayManagement.Implementation;
using Orchard.DisplayManagement;
using Orchard.Tests.Stubs;
using Orchard;
using Orchard.Environment;
using Downplay.Mechanics.Services;
using Moq;
using Orchard.ContentManagement;
using Orchard.ContentManagement.MetaData;
using Orchard.ContentManagement.MetaData.Models;
using Orchard.ContentManagement.MetaData.Builders;
using Orchard.Core.Title.Models;
using Downplay.Mechanics.Models;
using Orchard.Security;
using Orchard.Security.Permissions;
using Orchard.Localization;
using Orchard.Core.Title.Drivers;
using Orchard.ContentManagement.Drivers;
using Downplay.Mechanics.Drivers;
using Downplay.Mechanics.Handlers;
using Orchard.ContentManagement.Records;
using Downplay.Mechanics.Framework;
using Orchard.Caching;
using Orchard.ContentManagement.Drivers.Coordinators;
using Downplay.Origami.Services;
using Orchard.UI.Notify;
using Orchard.Core.Contents.Drivers;
using Orchard.ContentManagement.Handlers;
using Orchard.Core.Contents.Extensions;
using System.Diagnostics;
namespace Downplay.Science.Tests.Mechanics {
    public abstract class MechanicsTestsBase : DatabaseEnabledTestsBase {

        protected IMechanicsService _mechanics;
        protected IContentManager _contentManager;
        protected Mock<IContentDefinitionManager> _contentDefinitionManager;
        protected Mock<IAuthorizer> _authorizer;

        public override void Register(Autofac.ContainerBuilder builder) {

            _contentDefinitionManager = new Mock<IContentDefinitionManager>();
            builder.RegisterInstance(_contentDefinitionManager.Object);

            // Manager to test
            builder.RegisterType<MechanicsService>().As<IMechanicsService>();

            // Content management
            builder.RegisterModule(new ContentModule());
            builder.RegisterModule(new WorkContextModule()); // Allows Work<T> to, well, work
            builder.RegisterType<DefaultContentManager>().As<IContentManager>().SingleInstance();
            builder.RegisterType<DefaultContentManagerSession>().As<IContentManagerSession>();
            builder.RegisterType<DefaultContentDisplay>().As<IContentDisplay>();

            // Orchard core services
            builder.RegisterType<DefaultShapeTableManager>().As<IShapeTableManager>();
            builder.RegisterType<DefaultShapeFactory>().As<IShapeFactory>();
            builder.RegisterType<ShapeTableLocator>().As<IShapeTableLocator>();
            builder.RegisterType<StubWorkContextAccessor>().As<IWorkContextAccessor>();
            builder.RegisterType<StubOrchardServices>().As<IOrchardServices>();

            builder.RegisterType<StubCacheManager>().As<ICacheManager>();
            builder.RegisterType<Signals>().As<ISignals>();
            builder.RegisterType<ContentsDriver>().As<IContentPartDriver>();
            builder.RegisterType<ContentPartDriverCoordinator>().As<IContentHandler>();
            var notifier = new Mock<INotifier>();
            notifier.Setup(n => n.Add(It.IsAny<NotifyType>(), It.IsAny<LocalizedString>())).Callback<NotifyType, LocalizedString>((t, n) => {
                Trace.WriteLine(n.ToString());
            });
            builder.RegisterInstance(notifier.Object);

            // Misc mocks needed by some constructors
            builder.RegisterInstance(new Mock<IOrigamiService>().Object);
            builder.RegisterInstance(new Mock<IMechanicsDisplay>().Object);
            builder.RegisterType<TitlePartDriver>().As<IContentPartDriver>();
            // Need full namespace because conflict with TitlePartHandler in Framework
            builder.RegisterType<Orchard.Core.Title.Handlers.TitlePartHandler>().As<IContentHandler>();
            // We'll need the title aspect handler if we care about DisplayText
             builder.RegisterType<TitlePartHandler>().As<IContentHandler>();
            builder.RegisterType<SequencePartDriver>().As<IContentPartDriver>();
            builder.RegisterType<SequencePartHandler>().As<IContentHandler>();
            builder.RegisterType<SocketsContentPartDriver>().As<IContentPartDriver>();
            builder.RegisterType<MechanicsContentHandler>().As<IContentHandler>();
            builder.RegisterType<ConnectorPartDriver>().As<IContentPartDriver>();

            // Default Mechanics providers we need to test
            builder.RegisterType<DefaultSocketHandler>().As<ISocketHandler>();
            builder.RegisterType<DefaultConnectorHandler>().As<IConnectorHandler>();
            builder.RegisterType<SequenceSocketHandler>().As<ISocketHandler>();
            builder.RegisterType<SequenceConnectorHandler>().As<IConnectorHandler>();
            
            // Permissive authorizer
            _authorizer = new Mock<IAuthorizer>();
            _authorizer.Setup(auth => auth.Authorize(It.IsAny<Permission>(), It.IsAny<IContent>(), It.IsAny<LocalizedString>())).Returns(true);
            _authorizer.Setup(auth => auth.Authorize(It.IsAny<Permission>())).Returns(true);
            builder.RegisterInstance(_authorizer.Object);
            
        }

        protected override IEnumerable<Type> DatabaseTypes {
            get {
                return new[]{
                    typeof(SocketsPartRecord),
                    typeof(ConnectorPartRecord),
                    typeof(TitlePartRecord),
                    typeof(SequencePartRecord),
                    typeof(ContentTypeRecord),
                    typeof(ContentItemRecord),
                    typeof(ContentItemVersionRecord)
                };
            }
        }

        public override void Init() {
            base.Init();

            _mechanics = _container.Resolve<IMechanicsService>();
            // _services = _container.Resolve<IOrchardServices>();
            _contentManager = _container.Resolve<IContentManager>();
        }

        protected const string DefaultProductName = "Product";
        protected const string DefaultProductToCategoryName = "ProductToCategory";
        protected const string DefaultCategoryToProductName = "CategoryToProduct";
        protected const string DefaultCategoryName = "Category";
        protected const string DefaultMiscName = "Misc";

        protected void InitializeContentTypes(bool draftableSockets = false, bool draftableConnectors = false) {

            // Register the types and obtain them
            ContentTypeDefinition productType = new ContentTypeDefinitionBuilder()
                .Named(DefaultProductName)
                .WithPart("SocketsPart")
                .WithPart("TitlePart")
                .Draftable(draftableSockets)
                .Build();

            ContentTypeDefinition productToCategoryType = new ContentTypeDefinitionBuilder()
                .Named(DefaultProductToCategoryName)
                .WithPart("ConnectorPart", part => {
                    part.WithSetting("ConnectorTypePartSettings.InverseConnectorType", "CategoryToProduct")
                        .WithSetting("ConnectorTypePartSettings.AllowMany", "false") // Only one category on a product
                        .WithSetting("ConnectorTypePartSettings.AllowNone", "false") // One and one only
                        .WithSetting("ConnectorTypePartSettings.AllowedContentLeft", "Product")
                        .WithSetting("ConnectorTypePartSettings.AllowedContentRight", "Category")
                        ;
                })
                .WithPart("TitlePart")
                .Draftable(draftableConnectors)
                .Build();

            ContentTypeDefinition categoryToProductType = new ContentTypeDefinitionBuilder()
                .Named(DefaultCategoryToProductName)
                .WithPart("ConnectorPart", part => {
                    part
                        .WithSetting("ConnectorTypePartSettings.InverseConnectorType", "ProductToCategory")
                        .WithSetting("ConnectorTypePartSettings.AllowMany", "true") // Multiple products in category
                        .WithSetting("ConnectorTypePartSettings.AllowNone", "true") // Can have none
                        .WithSetting("ConnectorTypePartSettings.AllowedContentLeft", "Category")
                        .WithSetting("ConnectorTypePartSettings.AllowedContentRight", "Product")
                        ;
                })
                .WithPart("SequencePart")
                .Draftable(draftableConnectors)
                .Build();

            ContentTypeDefinition categoryType = new ContentTypeDefinitionBuilder()
                .Named(DefaultCategoryName)
                .WithPart("SocketsPart")
                .WithPart("TitlePart")
                .Draftable(draftableSockets)
                .Build();

            // A misc type that won't be connected so we can test things *don't* work
            ContentTypeDefinition miscType = new ContentTypeDefinitionBuilder()
                .Named(DefaultMiscName)
                .Build();

            var types = new List<ContentTypeDefinition> { productType, productToCategoryType, categoryToProductType, categoryType, miscType };

            _contentDefinitionManager.Setup(contentDefinitionManager => contentDefinitionManager.ListTypeDefinitions())
                .Returns(types);
            _contentDefinitionManager.Setup(contentDefinitionManager => contentDefinitionManager.GetTypeDefinition(It.IsAny<String>()))
                .Returns<string>(it=>types.FirstOrDefault(t=>t.Name==it));
        }


    }
}
